﻿using System.IO;
using Castle.Windsor;
using CommonServiceLocator.WindsorAdapter;
using Microsoft.Practices.ServiceLocation;
using MvcContrib.Castle;
using NHibernate.Cfg;
using NHibernate.Event;
using NHibernate.Tool.hbm2ddl;
using Peak.Core;
using Peak.Data;
using Peak.Web.Controllers.Binders;
using Peak.Web.Controllers.Models;
using SharpArch.Data.NHibernate;
using SharpArch.Web.NHibernate;
using SharpArch.Web.Areas;
using SharpArch.Web.ModelBinder;
using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Reflection;
using Peak.Web.Controllers;
using Peak.Data.NHibernateMaps;
using Peak.Web.CastleWindsor;
using Peak.Web.Controllers.Services;

namespace Peak.Web
{
  // Note: For instructions on enabling IIS6 or IIS7 classic mode,
  // visit http://go.microsoft.com/?LinkId=9394801

  public class MvcApplication : HttpApplication
  {
    static readonly log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    protected void Application_Start()
    {
      log4net.Config.XmlConfigurator.Configure();

      ViewEngines.Engines.Clear();
      ViewEngines.Engines.Add(new AreaViewEngine());

      RegisterModelBinders();

      InitializeServiceLocator();

      AreaRegistration.RegisterAllAreas();
      RouteRegistrar.RegisterRoutesTo(RouteTable.Routes);
    }

    private static void RegisterModelBinders()
    {
      ModelBinders.Binders.DefaultBinder = new SharpModelBinder();

      ModelBinders.Binders.Add(typeof(Project), new ProjectBinder());
      ModelBinders.Binders.Add(typeof(StoryFilterModel), new StoryFilterModelBinder());
      ModelBinders.Binders.Add(typeof(TaskFilterModel), new TaskFilterModelBinder());
      ModelBinders.Binders.Add(typeof(DashboardFilterModel), new DashboardFilterModelBinder());
      ModelBinders.Binders.Add(typeof(TimeSpan), new TimeSpanBinder());
    }   

    /// <summary>
    /// Instantiate the container and add all Controllers that derive from
    /// WindsorController to the container.  Also associate the Controller
    /// with the WindsorContainer ControllerFactory.
    /// </summary>
    protected virtual void InitializeServiceLocator()
    {
      IWindsorContainer container = new WindsorContainer();
      ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container));

      container.RegisterControllers(typeof(ProjectController).Assembly);
      ComponentRegistrar.AddComponentsTo(container);

      ServiceLocator.SetLocatorProvider(() => new WindsorServiceLocator(container));
    }

    public override void Init()
    {
      base.Init();

      // The WebSessionStorage must be created during the Init() to tie in HttpApplication events
      webSessionStorage = new WebSessionStorage(this);     
    }

    /// <summary>
    /// Due to issues on IIS7, the NHibernate initialization cannot reside in Init() but
    /// must only be called once.  Consequently, we invoke a thread-safe singleton class to
    /// ensure it's only initialized once.
    /// </summary>
    protected void Application_BeginRequest(object sender, EventArgs e)
    {
      NHibernateInitializer.Instance().InitializeNHibernateOnce(
          () => InitializeNHibernateSession());
    }

    /// <summary>
    /// If you need to communicate to multiple databases, you'd add a line to this method to
    /// initialize the other database as well.
    /// </summary>
    private void InitializeNHibernateSession()
    {
      try
      {
        bool initDb = false;
        if (!File.Exists(Server.MapPath("~/App_Data/peak.db")))
        {
          File.Create(Server.MapPath("~/App_Data/peak.db"));
          initDb = true;
        }

        Configuration cfg = NHibernateSession.Init(
          webSessionStorage,
          new string[] { Server.MapPath("~/bin/Peak.Data.dll") },
          new AutoPersistenceModelGenerator().Generate(),
          Server.MapPath("~/NHibernate.config"));

        cfg.EventListeners.PreLoadEventListeners = new IPreLoadEventListener[] { new PreLoadEventListener() };
        cfg.EventListeners.PostLoadEventListeners = new IPostLoadEventListener[] { new PostLoadEventListener() };

        new SchemaUpdate(cfg).Execute(null, true);
        if (initDb)
        {
          InitDb init = ServiceLocator.Current.GetInstance<InitDb>();
          init.Initialize();
        }
      }
      catch (Exception e)
      {
        if (_log.IsErrorEnabled)
        {
          _log.Error("Error: mappath:" + Server.MapPath("NHibernate.config"), e);
        }
        throw;
      }
    }

    protected void Application_Error(object sender, EventArgs e)
    {
      // Useful for debugging
      Exception ex = Server.GetLastError();
      ReflectionTypeLoadException reflectionTypeLoadException = ex as ReflectionTypeLoadException;
      if (_log.IsErrorEnabled)
        _log.Error("Error: ", ex);
    }

    private WebSessionStorage webSessionStorage;
  }
}